package cn.flkj.ecdsaca;

import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Base64;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;


/**
 * @author : 州长在手 2021/3/8 上午11:50
 */
public class EcdsaCa {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    /**
     * Parse X509 PEM to Certificate
     *
     * @param cert PEM 不需要掐头去尾
     * @return
     */
    public static X509Certificate ParseX509PEMtoCertificate(String cert) {
        byte[] cer = cert.getBytes();
        CertificateFactory certificateFactory = null;
        try {
            certificateFactory = CertificateFactory.getInstance("X509", "BC");
        } catch (CertificateException | NoSuchProviderException e) {
            e.printStackTrace();
        }
        InputStream inputStream = new ByteArrayInputStream(cer);
        try {
            X509Certificate caRootCert = (X509Certificate) certificateFactory.generateCertificate(inputStream);
            return caRootCert;
        } catch (CertificateException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param sk PEM  直接丢进去
     * @return
     */
    public static PrivateKey ParseSKtoPrivateKey(String sk) {
        int start = sk.indexOf("-----", 5);
        int end = sk.indexOf("-----", start + 5);
        byte[] privSk = Base64.decode(sk.substring(start + 5, end));
        KeyFactory keyFactory = null;
        try {
            keyFactory = KeyFactory.getInstance("EC");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privSk);
        PrivateKey caPrivateKey = null;
        try {
            caPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            return null;
        }
        return caPrivateKey;
    }

    /**
     * 整理JAVA类的私钥为64字节长度字符串
     * @param cert
     * @return
     */
    public static String modifyCertToPem(X509Certificate cert){
        try {
            String content = Base64.toBase64String(cert.getEncoded());
            String START = "-----BEGIN CERTIFICATE-----\n";
            String END ="-----END CERTIFICATE-----";
            return START + modifyCertToPemHelper(content)+END;
        } catch (CertificateEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
    private static String modifyCertToPemHelper(String subContent){
        // 递归结束条件，就是不递归了
        if (subContent.length() == 0){
            return  subContent;
        }
        if (subContent.length() < 64 ){
            return  subContent+"\n";
        }
        String start64 = subContent.substring(0,64);
        String rest = subContent.substring(64);
        // 一定要相信递归函数返回的结果就是子问题需要的结果
        String modifyResult = modifyCertToPemHelper(rest);
        // 然后使用子问题的结果，返回上一层递归需要的结果
        return start64 +"\n" + modifyResult;
    }

    public static X509Certificate CertRenewal(X509Certificate caCert, PrivateKey caPrivateKey, X509Certificate subCert){
        try{
            //使用 SHA256 算法的 ECDSA 密钥对
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256withECDSA").setProvider("BC").build(caPrivateKey);
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
                    caCert,
                    subCert.getSerialNumber(),
                    new Date(System.currentTimeMillis()),// 颁发证书的时间这里是1年 * 10
                    new Date(System.currentTimeMillis() + 31536000000L * 10),
                    subCert.getSubjectX500Principal(),
                    subCert.getPublicKey()
            );

            // ------------------------------------------------ 证书用途 ------------------------------------------------
            KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
                    | KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);
            KeyPurposeId[] extendedKeyUsages =  new KeyPurposeId[] {KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth};
            JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
            certGen.addExtension(Extension.authorityKeyIdentifier, false,
                    extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(caCert.getPublicKey().getEncoded())));
            // 基础约束,具有基本约束：证书颁发机构等于true的X509证书不能用作标识
            BasicConstraints  basicConstraints = new BasicConstraints(false);
            certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
            // 关键用法
            certGen.addExtension(Extension.keyUsage, true, usage);
            ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
            certGen.addExtension(Extension.extendedKeyUsage, false, xku);
            // -------------------------------------------------- End --------------------------------------------------

            X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certGen.build(sigGen));
            cert.checkValidity(new Date());
            cert.verify(caCert.getPublicKey());
            ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
            cert = (X509Certificate) fact.generateCertificate(bIn);

            return cert;
        }catch (Exception e){
            e.printStackTrace();
            return  null;
        }
    }
}
